﻿using System;
using System.Collections.Generic;
using System.Linq;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Runtime.Caching;
using Abp.Zero.Configuration;
using KelonProud.Administration.Domain.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using Abp;
using Microsoft.EntityFrameworkCore;

namespace KelonProud.Administration.Managers.Authorization
{
    public class RoleManager : AbpRoleManager<Role, User>
    {
        private readonly IRepository<Role> _roleRepository;
        private readonly IRepository<UserRole, long> _userRoleRepository;
        private readonly IRepository<NavigationRole> _navRoleRepository;
        private readonly IRepository<AuthorityRole> _roleAuthRepository;
        private readonly ICurrentUnitOfWorkProvider _currentUnitOfWork;

        public RoleManager(
            RoleStore store,
            IEnumerable<IRoleValidator<Role>> roleValidators,
            ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors,
            ILogger<AbpRoleManager<Role, User>> logger,
            IPermissionManager permissionManager,
            ICacheManager cacheManager,
            IUnitOfWorkManager unitOfWorkManager,
            IRoleManagementConfig roleManagementConfig, RoleStore roleStore,
            IRepository<UserRole, long> userRoleRepository,
            IRepository<NavigationRole> navRoleRepository,
            IRepository<AuthorityRole> roleAuthRepository,
            IRepository<Role> roleRepository, ICurrentUnitOfWorkProvider currentUnitOfWork)
            : base(
                store,
                roleValidators,
                keyNormalizer,
                errors, logger,
                permissionManager,
                cacheManager,
                unitOfWorkManager,
                roleManagementConfig)
        {
            RoleStore = roleStore;
            _navRoleRepository = navRoleRepository;
            _roleAuthRepository = roleAuthRepository;
            _roleRepository = roleRepository;
            _currentUnitOfWork = currentUnitOfWork;
            _userRoleRepository = userRoleRepository;
        }

        protected RoleStore RoleStore { get; private set; }

        public IQueryable<Role> Query => RoleStore.Roles;

        public IQueryable<UserRole> UserRoles => _userRoleRepository.GetAll();

        public IQueryable<NavigationRole> ModuleRoles => _navRoleRepository.GetAll();

        public IQueryable<AuthorityRole> AutRoles => _roleAuthRepository.GetAll();

        public IEnumerable<UserRole> CurrentUserRoles => UserRoles.Where(w => w.UserId == AbpSession.UserId);

        public override Task<Role> GetRoleByIdAsync(int roleId)
        {
            return Query.FirstOrDefaultAsync(f=>f.Id==roleId);
        }

        public Task<int> SaveRoleAsync(Role role)
        {
            CheckRoleExits(role);

            var result = _roleRepository.InsertOrUpdateAndGetId(role);

            _currentUnitOfWork.Current.SaveChanges();

            return Task.FromResult(result);
        }

        public void DeleteRole(Role role)
        {
            if(role!=null)
                _roleRepository.Delete(role);
        }

        public void SetNavigations(Role role, string[] navIds)
        {
            if (role.Navigations == null)
                role.Navigations = new List<NavigationRole>();

            foreach (var navRole in role.Navigations.ToList())
            {
                if (navIds.All(navId => navId != navRole.NavigationId.ToString()))
                {
                    _navRoleRepository.Delete(navRole);
                }
            }

            //Add to added roles
            foreach (var navId in navIds)
            {
                var all = role.Navigations.All(ur => ur.NavigationId != navId.ToInt32(0));
                if (all)
                {
                    _navRoleRepository.Insert(new NavigationRole()
                    {
                        NavigationId = navId.ToInt32(0),
                        RoleId = role.Id.ToInt32(0)
                    });
                }
            }
        }

        private void CheckRoleExits(Role role)
        {
            if (Query.Any(w => w.Id != role.Id && w.Name == role.Name))
                throw new Exception("角色编码【{0}】已存在".FormatWith(role.Name));

            if (Query.Any(w => w.Id != role.Id && w.DisplayName == role.DisplayName))
                throw new Exception("角色名称【{0}】已存在".FormatWith(role.DisplayName));
        }
    }

public class RoleStore : AbpRoleStore<Role, User>
    {
        public RoleStore(
            IUnitOfWorkManager unitOfWorkManager,
            IRepository<Role> roleRepository,
            IRepository<RolePermissionSetting, long> rolePermissionSettingRepository)
            : base(
                unitOfWorkManager,
                roleRepository,
                rolePermissionSettingRepository)
        {
        }
    }
}
